"""交易信号生成."""

from __future__ import annotations

import logging

import numpy as np

from .config import BacktestConfig

logger = logging.getLogger(__name__)


def adjust_thresholds_dynamically(
    probabilities: np.ndarray,
    default_buy: float = 0.6,
    default_sell: float = 0.4,
) -> tuple[float, float]:
    """根据预测概率分布动态调整买入/卖出阈值.
    
    策略：
    1. 如果预测概率分布偏左（大部分概率<0.5），降低买入阈值
    2. 如果预测概率分布偏右（大部分概率>0.5），提高卖出阈值
    3. 如果预测概率分布集中，调整阈值以确保有足够的交易信号
    
    Parameters
    ----------
    probabilities:
        预测概率数组
    default_buy:
        默认买入阈值
    default_sell:
        默认卖出阈值
        
    Returns
    -------
    (adjusted_buy_threshold, adjusted_sell_threshold)
    """
    if len(probabilities) == 0:
        return default_buy, default_sell
    
    mean_proba = float(np.mean(probabilities))
    median_proba = float(np.median(probabilities))
    std_proba = float(np.std(probabilities))
    q25 = float(np.percentile(probabilities, 25))
    q75 = float(np.percentile(probabilities, 75))
    
    # 计算预测概率的分布特征
    prob_below_05 = float(np.mean(probabilities < 0.5))  # 低于0.5的比例
    prob_above_05 = float(np.mean(probabilities > 0.5))  # 高于0.5的比例
    
    adjusted_buy = default_buy
    adjusted_sell = default_sell
    
    # 如果大部分预测概率低于0.5，说明模型预测偏保守，降低买入阈值
    if prob_below_05 > 0.7:
        # 使用75分位数作为买入阈值，确保有足够的买入信号
        adjusted_buy = min(0.65, max(0.5, q75))
        logger.debug(
            f"预测概率分布偏左（{prob_below_05:.1%} < 0.5），"
            f"调整买入阈值: {default_buy:.3f} -> {adjusted_buy:.3f}"
        )
    
    # 如果大部分预测概率高于0.5，说明模型预测偏乐观，提高卖出阈值
    if prob_above_05 > 0.7:
        # 使用25分位数作为卖出阈值，确保有足够的卖出信号
        adjusted_sell = max(0.35, min(0.5, q25))
        logger.debug(
            f"预测概率分布偏右（{prob_above_05:.1%} > 0.5），"
            f"调整卖出阈值: {default_sell:.3f} -> {adjusted_sell:.3f}"
        )
    
    # 如果预测概率分布集中（标准差小），调整阈值以确保有足够的交易信号
    if std_proba < 0.1:
        # 分布集中，使用中位数±0.1作为阈值
        adjusted_buy = min(0.65, max(0.5, median_proba + 0.1))
        adjusted_sell = max(0.35, min(0.5, median_proba - 0.1))
        logger.debug(
            f"预测概率分布集中（std={std_proba:.3f}），"
            f"调整阈值: 买入={adjusted_buy:.3f}, 卖出={adjusted_sell:.3f}"
        )
    
    # 确保买入阈值 > 卖出阈值
    if adjusted_buy <= adjusted_sell:
        gap = (adjusted_buy - adjusted_sell) / 2
        adjusted_buy = adjusted_buy + abs(gap) + 0.05
        adjusted_sell = adjusted_sell - abs(gap) - 0.05
    
    return adjusted_buy, adjusted_sell


class SignalGenerator:
    """交易信号生成器."""

    def __init__(self, config: BacktestConfig, use_dynamic_thresholds: bool = True) -> None:
        self.config = config
        self.use_dynamic_thresholds = use_dynamic_thresholds
        # 动态调整后的阈值（在生成信号时计算）
        self._current_buy_threshold = config.buy_threshold
        self._current_sell_threshold = config.sell_threshold

    def generate_signals(self, probabilities: np.ndarray) -> np.ndarray:
        """根据预测概率生成交易信号.

        Parameters
        ----------
        probabilities:
            上涨概率数组，shape=(n_samples,)，值域 [0, 1]

        Returns
        -------
        交易信号数组，shape=(n_samples,)
        - 1: 买入信号（p_up > buy_threshold）
        - -1: 卖出信号（p_up < sell_threshold）
        - 0: 中性/持有（其他情况）
        """
        # 如果启用动态阈值，根据预测分布调整阈值
        if self.use_dynamic_thresholds and len(probabilities) > 10:
            self._current_buy_threshold, self._current_sell_threshold = adjust_thresholds_dynamically(
                probabilities,
                default_buy=self.config.buy_threshold,
                default_sell=self.config.sell_threshold,
            )
        else:
            self._current_buy_threshold = self.config.buy_threshold
            self._current_sell_threshold = self.config.sell_threshold
        
        signals = np.zeros_like(probabilities, dtype=int)
        signals[probabilities > self._current_buy_threshold] = 1
        signals[probabilities < self._current_sell_threshold] = -1
        return signals

    def generate_position_changes(self, signals: np.ndarray, current_positions: np.ndarray) -> np.ndarray:
        """根据信号生成仓位变化.

        Parameters
        ----------
        signals:
            交易信号数组
        current_positions:
            当前仓位数组（1=满仓，0=空仓）

        Returns
        -------
        仓位变化数组
        - 1: 买入/满仓
        - -1: 卖出/清仓
        - 0: 不变
        """
        position_changes = np.zeros_like(signals, dtype=int)

        # 买入信号：如果当前空仓，则买入
        buy_mask = (signals == 1) & (current_positions == 0)
        position_changes[buy_mask] = 1

        # 卖出信号：如果当前满仓，则卖出
        sell_mask = (signals == -1) & (current_positions == 1)
        position_changes[sell_mask] = -1

        return position_changes

